using System;
using System.Diagnostics;

using org.fourzoas.noise.module;

namespace org.fourzoas.noise.module {
  /// <summary>
  /// Noise module that clamps the output value from a source module to a range of values.
  /// </summary>
  /// <remarks>
  /// <img src="file://C:/temp/noise/noise/doc/html/moduleclamp.png"/>
  /// <para>
  /// The range of values in which to clamp the output value is called the
  /// <i>clamping range</i>.
  ///</para>
  /// <para>
  /// If the output value from the source module is less than the lower
  /// bound of the clamping range, this noise module clamps that value to
  /// the lower bound.  If the output value from the source module is
  /// greater than the upper bound of the clamping range, this noise module
  /// clamps that value to the upper bound.
  ///</para>
  /// <para>
  /// To specify the upper and lower bounds of the clamping range, call the
  /// SetBounds() method.
  ///</para>
  /// <para>
  /// This noise module requires one source module.
  /// </para>
  /// </remarks>
  public class Clamp : ModifierModule {
    /// <summary>
    /// Default lower bound of the clamping range for the <see cref="Clamp"/> noise module.
    /// </summary>
    const double DEFAULT_CLAMP_LOWER_BOUND = -1.0;
    /// <summary>
    /// Default uppoer bound of the clamping range for the <see cref="Clamp"/> noise module.
    /// </summary>
    const double DEFAULT_CLAMP_UPPER_BOUND = 1.0;

    /// <summary>
    /// Default constructor.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The default lower bound of the clamping range is set to <see cref="DEFAULT_CLAMP_LOWER_BOUND"/>.
    /// </para>
    /// <para>
    /// The default upper bound of the clamping range is set to <see cref="DEFAULT_CLAMP_UPPER_BOUND"/>.
    /// </para>
    /// </remarks>
    public Clamp()
      : base(1) {
      m_lowerBound = DEFAULT_CLAMP_LOWER_BOUND;
      m_upperBound = DEFAULT_CLAMP_UPPER_BOUND;
    }

    /// <summary>
    /// Sets or returns the lower bound of the clamping range.
    /// </summary>
    /// <remarks>
    /// If the output value from the source module is less than the lower bound of the clamping range,
    /// this noise module clamps the value to the lower bound.
    /// </remarks>
    public double LowerBound {
      get { return m_lowerBound; }
      set { m_lowerBound = value; }
    }

    /// <summary>
    /// Sets or returns the upper bound of the clamping range.
    /// </summary>
    /// <remarks>
    /// If the output value from the source module is greater than the upper bound of the clamping range,
    /// this noise module clamps the value to the upprt bound.
    /// </remarks>
    public double UpprtBound {
      get { return m_upperBound; }
      set { m_upperBound = value; }
    }

    /// <summary>
    /// Returns the number of source modules required by this noise module.
    /// </summary>
    /// <returns>The number of source modules required by this noise module.</returns>
    public override int GetSourceModuleCount() {
      return 1;
    }

    /// <summary>
    /// Generates an output value given the coordinates of the specified input value.
    /// </summary>
    /// <param name="x">The <em>x</em> coordinate of the input value.</param>
    /// <param name="y">The <em>y</em> coordinate of the input value.</param>
    /// <param name="z">The <em>z</em> coordinate of the input value.</param>
    /// <returns>The output value.</returns>
    public override double GetValue(double x, double y, double z) {
      Debug.Assert(m_sourceModule[0] != null);

      double value = m_sourceModule[0].GetValue(x, y, z);
      if (value < m_lowerBound)
        return m_lowerBound;
      if (value > m_upperBound)
        return m_upperBound;
      return value;
    }

    /// <summary>
    /// Sets the lower and upper bounds of the clamping range.
    /// </summary>
    /// <param name="lowerBound">The lower bound.</param>
    /// <param name="upperBound">The upper bound.</param>
    /// <exception cref="ArgumentOutOfRangeException">Thrown if lowerBound is greater than upperBound</exception>
    /// <remarks>
    /// <para>
    /// If the output value from the source module is less than the lower
    /// bound of the clamping range, this noise module clamps that value
    /// to the lower bound.  If the output value from the source module
    /// is greater than the upper bound of the clamping range, this noise
    /// module clamps that value to the upper bound.
    /// </para>
    /// <h5>Preconditions</h5>
    /// <para>
    /// The lower bound must be less than or equal to the upper bound.
    /// </para>
    /// </remarks>
    public void SetBounds(double lowerBound, double upperBound) {
      if (lowerBound > upperBound)
        throw new ArgumentOutOfRangeException("lowerBound");
      m_lowerBound = lowerBound;
      m_upperBound = upperBound;
    }

    /// <summary>
    /// Lower bound of the clamping range.
    /// </summary>
    protected double m_lowerBound;
    /// <summary>
    /// Upper bound of the clamping range.
    /// </summary>
    protected double m_upperBound;
  }
}